//==========================================================================
// Copyright (c) 2000-2008,  Elastos, Inc.  All Rights Reserved.
//==========================================================================

/*
 * Copyright (c) 1997, 1998
 *    Bill Paul <wpaul@ctr.columbia.edu>.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by Bill Paul.
 * 4. Neither the name of the author nor the names of any co-contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY Bill Paul AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL Bill Paul OR THE VOICES IN HIS HEAD
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
 * THE POSSIBILITY OF SUCH DAMAGE.
 *
 */

//#pragma warning(disable:4133)  //subtract two pointers of different types
//#pragma warning(disable:4700)

#include <eladef.h>
#include <util/kprint.h>
#include "rtl8139.h"
#include <string.h>
#include <ddk.h>
#include "rtl4if.h"

//#define _ETHER_DEBUG_

/*
 * Default to using PIO access for this driver. On SMP systems,
 * there appear to be problems with memory mapped mode: it looks like
 * doing too many memory mapped access back to back in rapid succession
 * can hang the bus. I'm inclined to blame this on crummy design/construction
 * on the part of RealTek. Memory mapped mode does appear to work on
 * uniprocessor systems though.
 */


//static int Rtl8139_Encap(netdrv *, struct mbuf *);
static void rtl_8139_rxeof(netdrv *);
static void rtl_8139_txeof(netdrv *);
static void rtl_8139_stop(netdrv *);
extern void rtl_8139_stutdown(netdrv *pThis);
static void rtl_8139_eeprom_putbyte(netdrv *, int);
static void rtl_8139_eeprom_getword(netdrv *, int, UInt16 *);
static void rtl_8139_read_eeprom(netdrv *, unsigned char *, int, int, int);

//static UInt8 Rtl8139_Calchash(unsigned char *);
static void rtl_8139_setmulti(netdrv *);
static void rtl_8139_reset(netdrv *);
static int  rtl_8139_var_init(netdrv *);

static int  rtl_8139initalize(netdrv *  pThis);
int  rtl_8139xmit(netdrv*  pThis);
//ECode  rtl8139Ioctl(netdrv* pThis, UInt32 command);
static void  __cdecl irhan(irq_t , void *, InterruptContext *);
//Boolean   RTL8139Interrupt(void*  pThis);

//void   RTL8139Watchdog(netdrv* pThis);
//extern int usnetEntry();
extern void lwipentry();
EXTERN Thread *CreateSystemThread(
        threadroutine_t threadroutine, void *pvParameter,
        ScheduleClass *pScheduleClass, uint_t uSchedulePolicy);

static unsigned int mroundup2order(unsigned int u);


#define EE_SET(x)                           \
    CSR_WRITE_1(pThis, RL_EECMD,            \
        CSR_READ_1(pThis, RL_EECMD) | x)

#define EE_CLR(x)                           \
    CSR_WRITE_1(pThis, RL_EECMD,            \
        CSR_READ_1(pThis, RL_EECMD) & ~x)

void delay(int n) {
    n = n*0x03f;
    while(n > 0) {
        n--;
    }
}


/*
 * Send a read command and address to the EEPROM, check for ACK.
 */
static void
rtl_8139_eeprom_putbyte(netdrv *pThis, int addr)
{
    register int d, i;

    d = addr | RL_EECMD_READ;

    /*
     * Feed in each bit and strobe the clock.
     */
    for (i = 0x400; i; i >>= 1) {
        if (d & i) {
            EE_SET(RL_EE_DATAIN);
        } else {
            EE_CLR(RL_EE_DATAIN);
        }
        delay(100);
        EE_SET(RL_EE_CLK);
        delay(150);
        EE_CLR(RL_EE_CLK);
        delay(100);
    }
    return;
}

/*
 * Read a word of data stored in the EEPROM at address 'addr.'
 */
static void
rtl_8139_eeprom_getword(netdrv *pThis, int addr, UInt16 *dest)
{
    register int  i;
    UInt16        word = 0;

    /*
     * Enter EEPROM access mode.
     */
    CSR_WRITE_1(pThis, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL);

    /*
     * Send address of word we want to read.
     */
    rtl_8139_eeprom_putbyte(pThis, addr);

    CSR_WRITE_1(pThis, RL_EECMD, RL_EEMODE_PROGRAM|RL_EE_SEL);

    /*
     * Start reading bits from EEPROM.
     */
    for (i = 0x8000; i; i >>= 1) {
        EE_SET(RL_EE_CLK);
        delay(50);
        if (CSR_READ_1(pThis, RL_EECMD) & RL_EE_DATAOUT)
            word |= i;
        EE_CLR(RL_EE_CLK);
        delay(50);
    }

    /* Turn off EEPROM access mode. */
    CSR_WRITE_1(pThis, RL_EECMD, RL_EEMODE_OFF);

    *dest = word;

    return;
}

u_short __stdcall n2hs(u_short netshort)
{
    return (u_short)(((netshort & 0xff) << 8) | ((netshort & 0xff00) >> 8));
}

/*
 * Read a sequence of words from the EEPROM.
 */
static void
rtl_8139_read_eeprom(netdrv *pThis, unsigned char * dest, int off, int cnt, int swap)
{
    int      i;
    UInt16   word = 0, *ptr;

    for (i = 0; i < cnt; i++) {
        rtl_8139_eeprom_getword(pThis, off + i, &word);
        ptr = (UInt16 *)(dest + (i * 2));
        if (swap)
            *ptr = n2hs(word);
        else
            *ptr = word;
    }

    return;
}


/*
 * Calculate CRC of a multicast group address, return the upper 6 bits.
 */
//static UInt8
//Rtl8139_Calchash(unsigned char * addr)
//{
//    UInt32    crc, carry;
//    int       i, j;
//    UInt8     c;
//
//    /* Compute CRC for the address value. */
//    crc = 0xFFFFFFFF; /* initial value */
//
//    for (i = 0; i < 6; i++) {
//        c = *(addr + i);
//        for (j = 0; j < 8; j++) {
//            carry = ((crc & 0x80000000) ? 1 : 0) ^ (c & 0x01);
//            crc <<= 1;
//            c >>= 1;
//            if (carry)
//                crc = (crc ^ 0x04c11db6) | carry;
//        }
//    }
//
//    /* return the filter bit position */
//    return(crc >> 26);
//}

/*
 * Program the 64-bit multicast hash filter.
 */
static void
rtl_8139_setmulti(netdrv *pThis)
{
//    struct ifnet_             *ifp = &pThis->if_handle->arpcom.ac_if;
//    struct arpcom_            *ac = &pThis->if_handle->arpcom;
//    struct ether_multi       *enm;
//    struct ether_multistep   step;
//    int            h = 0;
//    UInt32         hashes[2] = { 0, 0 };
    UInt32         rxfilt;
//    int            mcnt = 0;

    rxfilt = CSR_READ_4(pThis, RL_RXCFG);

//    if (ifp->if_flags & IFF_ALLMULTI || ifp->if_flags & IFF_PROMISC)
    {
        rxfilt |= RL_RXCFG_RX_MULTI;
        CSR_WRITE_4(pThis, RL_RXCFG, rxfilt);
        CSR_WRITE_4(pThis, RL_MAR0, 0xFFFFFFFF);
        CSR_WRITE_4(pThis, RL_MAR4, 0xFFFFFFFF);
        return;
    }

    /* first, zot all the existing hash bits */
//    CSR_WRITE_4(pThis, RL_MAR0, 0);
//    CSR_WRITE_4(pThis, RL_MAR4, 0);
//
//    /* now program new ones */
//    ETHER_FIRST_MULTI(step, ac, enm);
//    while (enm != NULL) {
//        mcnt++;
//        h = Rtl8139_Calchash(enm->enm_addrlo);
//        if (h < 32)
//            hashes[0] |= (1 << h);
//        else
//            hashes[1] |= (1 << (h - 32));
//        mcnt++;
//        ETHER_NEXT_MULTI(step, enm);
//     }
//
//    if (mcnt)
//        rxfilt |= RL_RXCFG_RX_MULTI;
//    else
//        rxfilt &= ~RL_RXCFG_RX_MULTI;
//
//    CSR_WRITE_4(pThis, RL_RXCFG, rxfilt);
//    CSR_WRITE_4(pThis, RL_MAR0, hashes[0]);
//    CSR_WRITE_4(pThis, RL_MAR4, hashes[1]);
//
//    return;
}

static void
rtl_8139_reset(netdrv *pThis)
{
    register int        i;

    CSR_WRITE_1(pThis, RL_COMMAND, RL_CMD_RESET);

    for (i = 0; i < RL_TIMEOUT; i++) {
        delay(10);
        if (!(CSR_READ_1(pThis, RL_COMMAND) & RL_CMD_RESET))
            break;
    }
    if (i == RL_TIMEOUT)
        kprintf("Error:rl%d: reset never completed!\n", pThis->rl_unit);

    return;
}

static unsigned int mroundup2order(unsigned int u)
{
    if(!(u>0))__asm__("int $3");

    if (u == 1) {
        return 0;
    }
    if(u > 1 && u <= 2) {
        return 1;
    }
    if(u > 2 && u <= 4) {
        return 2;
    }
    if(u > 4 && u <= 8) {
        return 3;
    }
    if(u > 8 && u <= 16) {
        return 4;
    }
    if(u > 16 && u <= 32) {
        return 5;
    }
    if(u > 32 && u <= 64) {
        return 6;
    }
    if(u > 64 && u <= 128) {
        return 7;
    }
    if(u > 128 && u <= 256) {
        return 8;
    }
    if(u > 256 && u <= 512) {
        return 9;
    }
    if(u > 512 && u <= 1024) {
        return 10;
    }
    if(u > 1024 && u <= 2048) {
        return 11;
    }
    if(u > 2048 && u <= 4096) {
        return 12;
    }
    if(u > 4096 && u <= 8192) {
        return 13;
    }
    if(u > 8192 && u <= 16384) {
        return 14;
    }
    if(u > 16384 && u <= 32768) {
        return 15;
    }
    if(u > 32768 && u <= 65536) {
        return 16;
    }
    if(u > 65536 && u <= 131072) {
        return 17;
    }
    if(u > 131072 && u <= 262144) {
        return 18;
    }
    if(u > 262144 && u <= 524288) {
        return 19;
    }
    if(u > 524288 && u <= 1048576) {
        return 20;
    }
    if(u > 1048576 && u <= 2097152) {
        return 21;
    }
    if(u > 2097152 && u <= 4194304) {
        return 22;
    }
    if(u > 4194304 && u <= 8388608) {
        return 23;
    }
    if(u > 8388608 && u <= 16777216) {
        return 24;
    }
    if(u > 16777216 && u <= 33554432) {
        return 25;
    }
    return 26;
}


//u_char    eaddr[ETHER_ADDR_LEN+1];
static int
rlinit(netdrv *pThis, int unit)
{

    int       rl_did = 0;
    int i = 0;
    /* Reset the adapter. */
    rtl_8139_reset(pThis);

    /*
     * Get station address from the EEPROM.
     */
    rtl_8139_read_eeprom(pThis, (unsigned char *)&(pThis->eaddr[0]), RL_EE_EADDR, 3, 0);

    /*
     * A RealTek chip was detected. Inform the world.
     */
    kprintf("rl%d: Ethernet address: ", unit);
    for(i = 0; i <= ETHER_ADDR_LEN-1; i++) {
        kprintf("%d %02x ", i, pThis->eaddr[i]);
    }
    kprintf("\n");

    pThis->rl_unit = unit;
    pThis->cur = pThis->last = 0;

    /*
     * Now read the exact device type from the EEPROM to find
     * out if it's an 8129 or 8139.
     */
    rtl_8139_read_eeprom(pThis, (unsigned char *)&rl_did, RL_EE_PCI_DID, 1, 0);

    if (rl_did == RT_DEVICEID_8139 || rl_did == ACCTON_DEVICEID_5030 ||
        rl_did == DELTA_DEVICEID_8139 || rl_did == ADDTRON_DEVICEID_8139 ||
        rl_did == DLINK_DEVICEID_530TXPLUS)
        pThis->rl_type = RL_8139;
    else if (rl_did == RT_DEVICEID_8129)
        pThis->rl_type = RL_8129;
    else {
        kprintf("Error:rl%d: unknown device ID: %x\n", unit, rl_did);
        return -1;
    }
    kprintf("rl%d: Device ID: %x\n", unit, rl_did);

    {
        char * pAddr;

#ifdef _ETHER_DEBUG_
    kprintf("%d %d\n", RL_RXBUFLEN + 1518, mroundup2order((RL_RXBUFLEN
        + 1518)/(4*1024)));
#endif

        pAddr = (char*)DzAllocKernelPages(
            mroundup2order((RL_RXBUFLEN + 1518 + 4*1024 - 1)/(4*1024)));
        if (pAddr == NULL) {
            kprintf("Error: Could not malloc.\n");
        }

        pThis->dmaaddr = (unsigned char *)pAddr;
    }
    return 0;
}

extern netdrv *
rtl_8139new(int *pPortBase, int *pIrq, int *pMedia, int deviceNo)
{
    netdrv *pThis;
    static int unit = 0;

    pThis = (netdrv *)malloc(sizeof(netdrv));
    if (NULL == pThis) {
        kprintf("Error:Could not allocate %d bytes\n", sizeof *pThis);
        return NULL;
    }
    bzero(pThis, sizeof(netdrv));
    pThis->databuf = new char[2000];
    if (NULL == pThis->databuf) {
        kprintf("Error:Could not allocat buffer for net driver!!!\n");
        free(pThis);
        return NULL;
    }
    pThis->buflength = 2000;

    pThis->iobase = *pPortBase;
    pThis->irq = *pIrq;

    DzRegisterIsr(pThis->irq, IPL10, irhan, pThis);
    if (rlinit(pThis, unit)) {
        kprintf("Init Card Error\n");
        DzUnregisterIsr(pThis->irq);
        delete [](pThis->databuf);
        free(pThis);
        return NULL;
    }
    unit++;

    pThis->pRcondition = new DzCondition();
    pThis->pWcondition = new DzCondition();
    pThis->pRmutex = new DzMutex();
    pThis->pWmutex = new DzMutex();
    pThis->pDMAmutex = new DzMutex();
    pThis->pISRevent = new DzEvent();
    pThis->membufused = 0;
    return pThis;
}

Int32 Ist(void *arg)
{
    netdrv *pThis = (netdrv*)arg;
    unsigned short status;

    WaitResult wr;
    while(1) {
        pThis->pISRevent->Wait(&wr, NULL);
        //kprintf("!! 1: %d !!\n", (int)pThis);

        for(;;) {
            status = CSR_READ_2(pThis, RL_ISR);
            //kprintf("?? 2: %d ??\n", (int)pThis);
            if(status)
                CSR_WRITE_2(pThis, RL_ISR, status);

            if((status & RL_INTRS) ==0)
                break;
//            kprintf("status %08x\n", status);
            if ((status & RL_ISR_RX_OK) || (status & RL_ISR_RX_ERR)) {
                rtl_8139_rxeof(pThis);
            }

            if ((status & RL_ISR_TX_OK) || (status & RL_ISR_TX_ERR)) {
                rtl_8139_txeof(pThis);
            }

            if (status & RL_ISR_SYSTEM_ERR) {
                rtl_8139_reset(pThis);
                rtl_8139initalize(pThis);
            }
        }

        rtl_8139xmit(pThis);
        /* Re-enable interrupts. */
        CSR_WRITE_2(pThis, RL_IMR, RL_INTRS);
    }
    return NOERROR;
}

extern int
rtl_8139_connect(void *softc, void **hardc, netdrv *pRtl, Thread **ppThread)
{
    *hardc = &(pRtl->eaddr[0]);
    kprintf("");
    Thread *pThread = CreateSystemThread((threadroutine_t)Ist, (void *)pRtl,
            g_pDefaultScheduleClass, g_uDefaultSchedulePolicy);
    *ppThread = pThread;
    if (NULL == pThread) {
        kprintf("*ERROR* failed to create kernelthread\n");
        return -1;
    }

    rtl_8139_reset(pRtl);
    rtl_8139initalize(pRtl);
    return 0;
}

/*
 * Initialize the transmit descriptors.
 */
static int
rtl_8139_var_init(netdrv *pThis)
{
    int i;
    for (i = 0; i < RL_TX_LIST_CNT; i++) {
        CSR_WRITE_4(pThis, RL_TXADDR0 + (i * sizeof(UInt32)), 0x0);
    }
    pThis->cur = pThis->last = 0;
    return(0);
}

int
rtl_8139initalize(netdrv* pThis)
{
    int            i;
    UInt32        rxcfg = 0;
    Address addr;

    /*
     * Cancel pending I/O and free all RX/TX buffers.
     */
    rtl_8139_stop(pThis);

    /* Init our MAC address */
    for (i = 0; i < ETHER_ADDR_LEN; i++) {
        CSR_WRITE_1(pThis, RL_IDR0 + i, pThis->eaddr[i]);
    }

    /* Init the RX buffer pointer register. */
    {
        UInt32 p = 0;

      	addr = (Address)pThis->dmaaddr;
        p = (UInt32)PHYSADDR((char *)addr); //Mac address is available

#ifdef _ETHER_DEBUG_
        kprintf("\n BBB addr = %08x p = %08x\n", addr, p);
#endif

        CSR_WRITE_4(pThis, RL_RXADDR, p);
    }

    /* Init descriptors. */
    rtl_8139_var_init(pThis);

    /*
     * Enable transmit and receive.
     */
    CSR_WRITE_1(pThis, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB);

    /*
     * Set the initial TX and RX configuration.
     */
    CSR_WRITE_4(pThis, RL_TXCFG, RL_TXCFG_CONFIG);
    CSR_WRITE_4(pThis, RL_RXCFG, RL_RXCFG_CONFIG);

    /* Set the individual bit to receive frames for this host only. */
    rxcfg = CSR_READ_4(pThis, RL_RXCFG);
    rxcfg |= RL_RXCFG_RX_INDIV;

    /* If we want promiscuous mode, set the allframes bit. */
//    if (ifp->if_flags & IFF_PROMISC) {
//        rxcfg |= RL_RXCFG_RX_ALLPHYS;
//        CSR_WRITE_4(pThis, RL_RXCFG, rxcfg);
//    } else
    {
        rxcfg &= ~RL_RXCFG_RX_ALLPHYS;
        CSR_WRITE_4(pThis, RL_RXCFG, rxcfg);
    }

    /*
     * Set capture broadcast bit to capture broadcast frames.
     */
//    if (ifp->if_flags & IFF_BROADCAST)
    {
        rxcfg |= RL_RXCFG_RX_BROAD;
        CSR_WRITE_4(pThis, RL_RXCFG, rxcfg);
    }
//     else {
//        rxcfg &= ~RL_RXCFG_RX_BROAD;
//        CSR_WRITE_4(pThis, RL_RXCFG, rxcfg);
//    }

    /*
     * Program the multicast filter, if necessary.
     */
    rtl_8139_setmulti(pThis);

    /*
     * Enable interrupts.
     */
    CSR_WRITE_2(pThis, RL_IMR, RL_INTRS);

    /* Set initial TX threshold */
    pThis->rl_txthresh = RL_TX_THRESH_INIT;

    /* Start RX/TX process. */
    CSR_WRITE_4(pThis, RL_MISSEDPKT, 0);

    /* Enable receiver and transmitter. */
    CSR_WRITE_1(pThis, RL_COMMAND, RL_CMD_TX_ENB|RL_CMD_RX_ENB);

    CSR_WRITE_1(pThis, RL_CFG1, RL_CFG1_DRVLOAD|RL_CFG1_FULLDUPLEX);

    return 0;
}


void __cdecl irhan(irq_t irq, void *pvDevice, InterruptContext *pContext)
{
    netdrv * pThis = (netdrv *)pvDevice;
    /* Disable interrupts. */
    CSR_WRITE_2(pThis, RL_IMR, 0x0000);
    pThis->pISRevent->NotifyByIsr();
}

int rtl_8139xmit(netdrv* pThis)
{

    UInt32  p;
    int     len;
    char *data_ptr;

    char *addr = pThis->databuf;

    WaitResult wr;
    pThis->pWmutex->Lock(&wr);
    if (((pThis->pRtlObject)->m_nodeWriteList).IsEmpty()) {
        pThis->pWmutex->Unlock();
        return 0;
    }
    Node *pnode = ((pThis->pRtlObject)->m_nodeWriteList).GetNode();
    pThis->pWmutex->Unlock();

//    kprintf("@cur=%d", pThis->cur);
//    while((pThis->cur + 1) % 4 == pThis->last);

    data_ptr = (char*)pnode->m_address;
    len = pnode->m_len;


    pThis->pDMAmutex->Lock(&wr);
    memset((void *)addr, 0, pThis->buflength);
    memcpy((void*)addr, (void*)data_ptr, len);
    pThis->pDMAmutex->Unlock();

    //PHYSADDR only transfer kernel addr
    p = (UInt32)PHYSADDR((char *)addr);

//    kprintf(" <%d_%d> ", pThis->cur, pThis->last);
    CSR_WRITE_4(pThis, (RL_TXADDR0 + 4 * pThis->cur), p);

    CSR_WRITE_4(pThis, (RL_TXSTAT0 + 4 * pThis->cur),
         RL_TXTHRESH(pThis->rl_txthresh) |
         (len < 1514 ? (len > 60 ? len : 60) : 1514));

    RL_INC(pThis->cur);
//    kprintf("cur=%d\n", pThis->cur);
    free (pnode->m_address);
    delete pnode;

    return 0;
}

/*
 * A frame has been uploaded: pass the resulting mbuf chain up to
 * the higher level protocols.
 *
 * You know there's something wrong with a PCI bus-master chip design
 * when you have to use m_devget().
 *
 * The receive operation is badly documented in the datasheet, so I'll
 * attempt to document it here. The driver provides a buffer area and
 * places its base address in the RX buffer start address register.
 * The chip then begins copying frames into the RX buffer. Each frame
 * is preceeded by a 32-bit RX status word which specifies the length
 * of the frame and certain other status bits. Each frame (starting with
 * the status word) is also 32-bit aligned. The frame length is in the
 * first 16 bits of the status word; the lower 15 bits correspond with
 * the 'rx status register' mentioned in the datasheet.
 *
 * Note: to make the Alpha happy, the frame payload needs to be aligned
 * on a 32-bit boundary. To achieve this, we cheat a bit by copying from
 * the ring buffer starting at an address two bytes before the actual
 * data location. We can then shave off the first two bytes using m_adj().
 * The reason we do this is because m_devget() doesn't let us specify an
 * offset into the mbuf storage space, so we have to artificially create
 * one. The ring is allocated in such a way that there are a few unused
 * bytes of space preceecing it so that it will be safe for us to do the
 * 2-byte backstep even if reading from the ring at offset 0.
 */
//inline ECode SetBreakPoint(
//        UInt32 uNo, BreakPointType type, void *pvAddress, UInt32 uLength)
//{
//    return GetCurrentProcess()->SetBreakPoint(uNo, type, pvAddress, uLength);
//}

void
rtl_8139_rxeof(netdrv *pThis)
{
    UInt32        total_len = 0;
    UInt32        rxstat;
    unsigned char *       rxbufpos;
    int           wrap = 0;
    UInt16        cur_rx;
    UInt16        limit;
    UInt16        rx_bytes = 0, max_bytes;
    WaitResult wr;

    /* 16 means 64bit sychronous singnal and 64bit buf ring*/
    cur_rx = (CSR_READ_2(pThis, RL_CURRXADDR) + 16) % RL_RXBUFLEN;

    /* Do not try to read past this point. */
    limit = CSR_READ_2(pThis, RL_CURRXBUF) % RL_RXBUFLEN;

    if (limit < cur_rx)
        max_bytes = RL_RXBUFLEN - (cur_rx - limit);
    else
        max_bytes = limit - cur_rx;

    while((CSR_READ_1(pThis, RL_COMMAND) & RL_CMD_EMPTY_RXBUF) == 0) {
        rxbufpos = pThis->dmaaddr + cur_rx;

        rxstat = *(UInt32 *)rxbufpos;

        /*
         * Here's a totally undocumented fact for you. When the
         * RealTek chip is in the process of copying a packet into
         * RAM for you, the length will be 0xfff0. If you spot a
         * packet header with this value, you need to stop. The
         * datasheet makes absolutely no mention of this and
         * RealTek should be shot for this.
         */
        if ((UInt16)(rxstat >> 16) == RL_RXSTAT_UNFINISHED)
            break;

        if (!(rxstat & RL_RXSTAT_RXOK)) {
//            kprintf("receive packet stat error\n");
            rtl_8139initalize(pThis);
            return;
        }

        /* No errors; receive the packet. */
        total_len = rxstat >> 16;
        /* 4  means the spzce of  total_len in rx*/
        rx_bytes += total_len + 4;

        /*
         * XXX The RealTek chip includes the CRC with every
         * received frame, and there's no way to turn this
         * behavior off (at least, I can't find anything in
         * the manual that explains how to do it) so we have
         * to trim off the CRC manually.
         */
        total_len -= ETHER_CRC_LEN;


        /*
         * Avoid trying to read more bytes than we know
         * the chip has prepared for us.
         */
        if (rx_bytes > max_bytes)
            break;

        //BUGBUG:maybe can be recover if wait a little time, but
        //the bug is hard to show up again
        if(total_len > 1514 || total_len < 60) {
//            kprintf("receive packet length error\n");
            rtl_8139initalize(pThis);
            return;
        }

        rxbufpos = pThis->dmaaddr +
            ((cur_rx + sizeof(UInt32)) % RL_RXBUFLEN);

        if (rxbufpos == (pThis->dmaaddr + RL_RXBUFLEN))
            rxbufpos = pThis->dmaaddr;

        wrap = (pThis->dmaaddr + RL_RXBUFLEN) - rxbufpos;

        pThis->pRmutex->Lock(&wr);
        pThis->membufused += total_len;
        pThis->pRmutex->Unlock();
        if (pThis->membufused > 5*1024*1024) {
            if (total_len > (UInt32)wrap) {
                cur_rx = (total_len - wrap + ETHER_CRC_LEN);
            }
            else {
                cur_rx += total_len + 4 + ETHER_CRC_LEN;
            }
            cur_rx = (cur_rx + 3) & ~3;
            CSR_WRITE_2(pThis, RL_CURRXADDR, cur_rx - 16);

            pThis->pRcondition->Pulse();
            kprintf("membufused too much\n");
            DzSleep(1, NULL);
            break;
        }
        /* Packet is good, try to get a memory block */
        unsigned char * p;
        p = (unsigned char *)malloc(total_len);
        if(p == NULL){
            kprintf("malloc failed+++++\n");
            return;
        }
        Node* pnode = new Node(p, total_len);

        memcpy(p, rxbufpos, total_len);
        if (total_len > (UInt32)wrap) {
            memcpy(p + wrap, pThis->dmaaddr, total_len - wrap);
            cur_rx = (total_len - wrap + ETHER_CRC_LEN);
        }
        else {
            cur_rx += total_len + 4 + ETHER_CRC_LEN;
        }

        /*
         * Round up to 32-bit boundary.
         */
        cur_rx = (cur_rx + 3) & ~3;
        CSR_WRITE_2(pThis, RL_CURRXADDR, cur_rx - 16);

        pThis->pRmutex->Lock(&wr);
        ((pThis->pRtlObject)->m_nodeReadList).InsertNode(pnode);
        pThis->pRcondition->Pulse();
        pThis->pRmutex->Unlock();

    }
    return;
}

/*
 * A frame was downloaded to the chip. It's safe for us to clean up
 * the list buffers.
 */
static void
rtl_8139_txeof(netdrv *pThis)
{
    UInt32        txstat;
    int           oldthresh;

    /*
     * Go through our tx list and free mbufs for those
     * frames that have been uploaded.
     */
     do {
//    kprintf(" {%d_%d} ", pThis->cur, pThis->last);
         txstat = CSR_READ_4(pThis, pThis->last*4 + RL_TXSTAT0);
         if (!(txstat & (RL_TXSTAT_TX_OK|
             RL_TXSTAT_TX_UNDERRUN|RL_TXSTAT_TXABRT))) {
             kprintf(" error recover\n");
             break;
             goto exit;
         }

         if (!(txstat & RL_TXSTAT_TX_OK)) {

             if ((txstat & RL_TXSTAT_TXABRT) ||
                     (txstat & RL_TXSTAT_OUTOFWIN)) {
                 CSR_WRITE_4(pThis, RL_TXCFG, RL_TXCFG_CONFIG);
             }

             oldthresh = pThis->rl_txthresh;
             /* error recovery */
             rtl_8139_reset(pThis);
             rtl_8139initalize(pThis);
             /*
              * If there was a transmit underrun,
              * bump the TX threshold.
              */
             if (txstat & RL_TXSTAT_TX_UNDERRUN)
                 pThis->rl_txthresh = oldthresh + 32;
             return;
         }
         RL_INC(pThis->last);
//         kprintf("last=%d cur=%d\n", pThis->last, pThis->cur);
     }while(pThis->cur != pThis->last);
exit:
    return;
}



/*
 * Stop the adapter and free any mbufs allocated to the
 * RX and TX lists.
 */
void
rtl_8139_stop(netdrv *pThis)
{
    CSR_WRITE_1(pThis, RL_COMMAND, 0x00);
    CSR_WRITE_2(pThis, RL_IMR, 0x0000);

    /*
     * Free the TX list buffers.
     */
    for(int i= 0; i <= 3; i++) {
	    CSR_WRITE_4(pThis, RL_TXADDR0 + i*4, 0x0);
    }
    return;
}

void
rtl_8139_stutdown(netdrv *pThis)
{
    CSR_WRITE_1(pThis, RL_COMMAND, 0x00);
    CSR_WRITE_2(pThis, RL_IMR, 0x0000);

    for(int i= 0; i <= 3; i++) {
	    CSR_WRITE_4(pThis, RL_TXADDR0 + i*4, 0x0);
    }
    return;
}


Node::Node(unsigned char*p, int len)
{
    m_address = p;
    m_len = len;
    Initialize();
}

void Node::InsertNode(Node*p)
{
    InsertFirst(p);
}

Node* Node::GetNode()
{
    Node* pnode = (Node*)Last();
    pnode->Detach();
    return pnode;
}
